package com.netty.server.cmd;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import lombok.Builder;
import lombok.Data;

import java.io.Serializable;

/**
 * 设备与服务器之间协议传输的基本指令对象
 *
 * @author: 王霄
 * @date: 2022/1/17 14:07
 **/
@Data
@Builder
public class ByteCmd implements Serializable {

    private Header header;

    private byte cmdType;

    private long cmdId;

    private boolean needAck;

    private byte[] body;

    public static ByteCmd deserialize(byte[] bs) {
        if (bs == null || bs.length == 0) {
            throw new RuntimeException("空数据包!");
        }
        ByteBuf buf = Unpooled.wrappedBuffer(bs);
        try {
            //1B 协议种类
            byte header = buf.readByte();
            //1B 协议类型
            byte cmdType = buf.readByte();
            //4B 协议ID
            long cmdId = buf.readUnsignedInt();
            //1B 协议是否需要ACK响应
            byte needAck = buf.readByte();
            //2B 协议体长度
            int bodyLen = buf.readUnsignedShort();
            //NB 协议体
            byte[] body = new byte[bodyLen];
            buf.readBytes(body);

            return ByteCmd.builder()
                    .header(Header.get(header))
                    .cmdType(cmdType)
                    .cmdId(cmdId)
                    .needAck(needAck == 0X01)
                    .body(body)
                    .build();
        } finally {
            buf.release();
        }
    }

    public byte[] serializer() {
        ByteBuf buf = ByteBufAllocator.DEFAULT.heapBuffer();
        try {
            //1B 协议种类
            buf.writeByte(header.getType());
            //1B 协议类型
            buf.writeByte(cmdType);
            //4B 协议ID
            buf.writeInt(Math.toIntExact(cmdId));
            //1B 协议是否需要ACK响应
            buf.writeByte(needAck ? 0X01 : 0X02);
            if (body != null) {
                //2B 协议体长度
                buf.writeShort(body.length);
                //NB 协议体
                buf.writeBytes(body);
            } else {
                buf.writeShort(0);
            }

            byte[] bs = new byte[buf.readableBytes()];
            buf.readBytes(bs);
            return bs;
        } finally {
            buf.release();
        }
    }

    public boolean isLogin() {
        return header == Header.EVENT && cmdType == (byte) 0X00;
    }
}
